/*
 * DocSelector.java
 *
 * Copyright (c) 2006-2007 Computer-Aided Integrated Systems Laboratory, 
 * St. Petersburg Institute for Informatics and Automation RAS http://cais.lisa.iias.spb.su
 */

package guislicer;

import wikipedia.util.StringUtilRegular;
import wikipedia.util.Encodings;

import Ontology.*;
//import com.sun.org.apache.bcel.internal.generic.ARRAYLENGTH;
import dbindex.DocTable;
import dbindex.ComparativeClass;
import dbindex.ComparativeAttr;
import dbindex.Language;
import topindex.Index;

import matching.LevenshteinDistance;
import matching.GraphComparator;
import matching.UnicodeVertexStringer;
import edu.uci.ics.jung.graph.decorators.VertexStringer;
import edu.uci.ics.jung.graph.Graph;
import edu.uci.ics.jung.io.PajekNetWriter;
import edu.uci.ics.jung.algorithms.shortestpath.UnweightedShortestPath;

import java.util.List;
import java.util.ArrayList;
import java.util.Vector;
import java.util.Collections;
import java.io.IOException;
import org.apache.axis.encoding.ser.ArraySerializer;


/** 
 *  
 */
public class DocSelector {
    
    /** Creates a new instance of DocSelector */
    public DocSelector() {
    }
    
    public static final List<DocTable> DOCTABLE_NULL_LIST = new ArrayList<DocTable>();
    
    public static void printSliceStatistics(WDSliceModeller WDSMTest) {
        System.out.println( "Slice:");
		System.out.println( "# of classes: " + WDSMTest.vResClasses.size() );
		System.out.println( "# of attributes: " + WDSMTest.vResAttr.size() );
		System.out.println( "# of pairs of classes and attributes: " + WDSMTest.vResClassAttr.size() );
		System.out.println( "# of hierarchical relations: " + WDSMTest.vResHier.size() );
		System.out.println( "# of associative relations: " + WDSMTest.vResAssoc.size() );
		System.out.println( "# of functional constraints: " + WDSMTest.vResFunc.size() );
		System.out.println( "Ontology:");
		System.out.println( "# of classes: " + WDSMTest.WDO.vClasses.size() );
		System.out.println( "# of attributes: " + WDSMTest.WDO.vAttributes.size() );
		System.out.println( "# of pairs of classes and attributes: " + WDSMTest.WDO.vClassAttr.size() );
		System.out.println( "# of hierarchical relations: " + WDSMTest.WDO.vWDHier.size() );
		System.out.println( "# of associative relations: " + WDSMTest.WDO.vWDAssoc.size() );
		System.out.println( "# of functional constraints: " + WDSMTest.WDO.vWDFunc.size() );
    }
    
    
    /** Creates the test ontology slice (abstract context)
     */
    public static WDSliceModeller createTestSlice (int iiLangID) 
    {
        //	IDs of extracted from request classes
		int [] iSelClasses;
		iSelClasses =  new int [1];
		//iSelClasses[0] = 63218;
        iSelClasses[0] = 84197; // class "Fire"
		//	IDs of extracted from request attributes
		int [] iSelAttributes;
		iSelAttributes =  new int [0];
		//	ID of ontology to be read
		int iOntoID;
		//iOntoID = 101; // Absent relevant documents for:Emergency_evacuation;
        iOntoID = 139; // Class not found: 63218
        
		WDSliceModeller slice_context = new WDSliceModeller( iOntoID, iSelClasses, iSelAttributes, false, iiLangID);
        
        if(Index.B_DEBUG) {
            printSliceStatistics(slice_context);
        }
        return slice_context;
    }
        
        
    public static String convertIntArraytoString(int[] a,String delimiter) {
        String s = "";
        if(null == a || 0 == a.length)
            return s;
        for(int i=0; i<a.length-1; i++) {
            s += a[i] + delimiter;
        }
        return s + a[a.length-1];
    }
    
    /** Creates ontology slice by set of classes & attributes indexed as relevant
     * to the document.
     *
     *  1. Takes indexed documents d by its doc_id.
     *  2. Takes list L of classes, attributes, relations from Index by document D.
     *  3* Builds slice S in WebDeso ontology by L (ontology slice for the indexed document).
     *
     *  3* In really it should be "context slice", but we have not it. In order to test 
     *      algorithm "Relevance estimation and selection of relevant documents from index",
     *      context slice is substituted by document slice.
     *
     * May be todo DocTable funcions:
     *  Takes id of Wikipedia indexed documents d by its 'title'.
     *  Takes id of local indexed documents d by its 'title'.
     */
    public static WDSliceModeller createSliceFromIndexByDocID (
                                java.sql.Connection     ksnetcontext_conn, Language lang,
                                int                     onto_id,
                                int                     doc_id,
                                boolean                 b_debug) 
    {
        // 1. 
        DocTable d_source = DocTable.GetByDocID(ksnetcontext_conn, doc_id, onto_id);
        
        // 2. 
        int[] class_id = ComparativeClass.getClassIDByDocID(ksnetcontext_conn, d_source.doc_id, onto_id);
        int[] attr_id  = ComparativeAttr.getAttrIDByDocID  (ksnetcontext_conn, d_source.doc_id, onto_id);
        if(b_debug) {
            System.out.println("Ontology fragment in Index has "+class_id.length+" classes, "+attr_id.length+" attributes.");
            System.out.println("Classes id are:" + convertIntArraytoString(class_id,", "));
            if(0 < attr_id.length) {
                System.out.println("Attributes id are:" + convertIntArraytoString(attr_id,", "));
            }
        }
        
        // 3.
        WDSliceModeller slice = null;
        if(0 < class_id.length + attr_id.length) { // don't create Slice for empty ontology fragment
            slice = new WDSliceModeller( onto_id, class_id, attr_id, 
                                false,      // false - not to print dump slice
                                lang.toInt());

            if(b_debug) {
                //System.out.println("Ontology slice has "+slice.vResClasses.size()+" classes, "+slice.vResAttr.size()+" attributes, and "+
                //        slice.vResHier.size()+" hier. relations.");
                printSliceStatistics(slice);
            }
        }
        
        return slice;
    }
    
    public final static List<DocTable> NULL_DOCTABLE_LIST = new ArrayList<DocTable>();
    
    /** Selects documents (sorted by relevance) for the ontology slice.
     * Index should be built before this func execution.
     *
     * @param dump_filepath  where to store file with the graph in Pajek format, skipped if it is null
     */
    public static List<DocTable> select(
            java.sql.Connection conn,Language lang,
            WebDESOOntology wdo,int onto_id,
            Vector slice_vClassAttr,Vector slice_vClasses,Vector slice_vAttributes,
            Vector slice_vWDHier,Vector slice_vWDAssoc,Vector slice_vWDFunc,Vector slice_vWDTax,
            String dump_filepath)
    {
        if(null == wdo) {
            System.out.println("Error: WebDESOOntology 'wdo' is null (DocSelector.select)");
            return DOCTABLE_NULL_LIST;
        }
        if(null == conn) {
            System.out.println("Error: Wikipedia Connection 'conn' is null (DocSelector.select)");
            return DOCTABLE_NULL_LIST;
        }
        
        PajekNetWriter pnw = null;
        VertexStringer vs  = null;
        
        // 0. Creates global graph with all classes, attributes, relations of the ontology
        Junger j_all = new Junger();
        //Graph g_all = j_all.createGraph(wdo.vClassAttr,wdo.vClasses,wdo.vAttributes,wdo.vWDHier,wdo.vWDAssoc,wdo.vWDFunc,wdo.vWDTax);
        
        Vector vClassesObjects = new Vector();
        Vector vAttrObjects    = new Vector();
        vClassesObjects.addAll( wdo.htClasses   .values() );
        vAttrObjects   .addAll( wdo.htAttributes.values() );
        
        Graph g_all = j_all.createGraph(
                wdo.vClassAttr,
                vClassesObjects,    // wdo.vClasses,
                vAttrObjects,       // wdo.vAttributes
                wdo.vWDHier,wdo.vWDAssoc,wdo.vWDFunc,wdo.vWDTax);
        if(null == g_all) {
            return NULL_DOCTABLE_LIST;
        }
        UnweightedShortestPath usp_all = new UnweightedShortestPath(g_all);
        
        // 1. Fills jung graph A by data from vectors (ontology slice).
        // Graph A corresponds to context (ontology slice)
        Junger j1 = new Junger();
        Graph g1 = j1.createGraph(
                slice_vClassAttr,slice_vClasses,slice_vAttributes,
                slice_vWDHier,slice_vWDAssoc,slice_vWDFunc,slice_vWDTax);
        UnweightedShortestPath usp1 = new UnweightedShortestPath(g1);
        
        if(null != dump_filepath) {
            pnw = new PajekNetWriter();
            String fn  = dump_filepath+"_all_ontology.net";
            String fn1 = dump_filepath+"_1_slice_context.net";
            try {
                vs = new UnicodeVertexStringer();
                pnw.save(g_all, fn, vs, null);;
                pnw.save(g1,    fn1, vs, null);;
            } catch (IOException e) {
                System.out.println("Error: failed to write graph to file '"+fn+"' (DocSelector.select): " + e.getMessage());
            }
        }
    
        
        // 2. Selects slices from Index. Fills jung graphs B1, B2....
        
        // 2.1 gets all docs by class id and attr id
        int[] class_id = new int[slice_vClasses.size()];
        int[]  attr_id = null;
        for(int i=0;i<slice_vClasses.size();i++) {
            class_id[i] = ((Classes)slice_vClasses.get(i)).getClassID();
        }
        
        if(null != slice_vAttributes) {
            attr_id = new int[slice_vAttributes.size()];
            for(int i=0;i<slice_vAttributes.size();i++) {
                attr_id[i] = ((Attributes)slice_vAttributes.get(i)).getAttrID();
            }
        }
        List<DocTable> docs = DocTable.getDocsWithUrlByClassIDAttrID(conn, lang, class_id, attr_id, onto_id);
        if(null == docs || 0 == docs.size())
            return DOCTABLE_NULL_LIST;
        
        List<DocTable> res_docs = new ArrayList<DocTable>();
        
        // 3. Compares WebDeso slice (graph A) and slice from index (corresponds to doc_id) (B1, B2, ...)
        LevenshteinDistance lev = new LevenshteinDistance();
        lev.use_dist_matrix = true;
        lev.setContstraintType(LevenshteinDistance.TYPE_SOFT);        
        GraphComparator gc = new GraphComparator();
        
        for(DocTable d:docs) {
        //for(int i=0;i<docs.size();i++) {
        //for(int i=0;i<3;i++) {
        //for(int i=0;i<4;i++) {
            //{ DocTable d = docs.get(i);
            //{ DocTable d = docs.get(3);
            // 1.
            //int[] doc_class_id = ComparativeClass.getClassIDByDocID(conn, d.doc_id, onto_id);
            //Graph g2 = j_all.getSubgraphByClassesID(doc_class_id);
            // 2.
            /*
            WDSliceModeller slice_doc = DocSelector.createSliceFromIndexByDocID (
                                                conn, onto_id, lang.toInt(),
                                                d.doc_id, false); //debug
            Graph g2 = junger.createGraph(  slice_doc.vResClassAttr,
                                            slice_doc.vResClasses,  
                                            slice_doc.vResAttr,
                                            slice_doc.vResHier,
                                            slice_doc.vResAssoc, 
                                            slice_doc.vResFunc,
                                            null); // taxonomical
            */
            // 3.
            String _fn_debug = null;
            if(null != dump_filepath) {
                _fn_debug = dump_filepath +
                    StringUtilRegular.encodeRussianToLatinitsa(d.title, Encodings.enc_java_default, Encodings.enc_int_default);
            }
                            
            Graph g2 = Junger.createGraphMinSubsetOfConnectedClasses(
                    conn, d.doc_id, onto_id, 
                    g_all, usp_all,
                    j_all,
                    _fn_debug);
            
            if(null != g2) {
                UnweightedShortestPath usp2 = new UnweightedShortestPath(g2);
                
                if(null != dump_filepath) {
                    String fn = dump_filepath +
                            StringUtilRegular.encodeRussianToLatinitsa(d.title, Encodings.enc_java_default, Encodings.enc_int_default)
                             + ".net";
                    try {
                        pnw.save(g2, fn, vs, null);
                    } catch (IOException e) {
                        System.out.println("Error: failed to write graph to file '"+fn+"' (DocSelector.select): " + e.getMessage());
                    }
                }
                
                //d.relevance = d.sim * gc.compare(g1, g2, lev);
                //d.relevance = gc.compare(g1, g2, lev);
                d.relevance = gc.compare(g1, g2, usp1, usp2, lev);
                res_docs.add(d);
            }
        }
        DocTable.normalizeRelevance(docs);
        System.out.println("Levenshtein distance was calculated "+lev.count_calc+" times.");
        
        // 4. Sorts docs by value of similarity
        Collections.sort(res_docs, DocTable.DIST_ORDER);
        return res_docs;
    }
    
}
